package org.summerframework.component.security.core.service.impl;

import org.summerframework.component.security.Constants;
import org.summerframework.component.security.LoginData;
import org.summerframework.component.security.core.config.LoginProperties;
import org.summerframework.component.security.core.event.LogoutEvent;
import org.summerframework.component.security.core.service.LoginLogService;
import org.summerframework.component.security.core.service.LoginManage;
import org.summerframework.component.security.core.service.LogoutService;
import org.summerframework.component.security.enumeration.LoginFromEnum;
import org.summerframework.component.security.enumeration.LoginTypeEnum;
import org.summerframework.component.security.enumeration.LogoutTypeEnum;
import org.summerframework.core.util.SpringContextUtil;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;


/**
 * 微信登录
 * <p>
 * {@link LoginManage} 当 {@link LoginTypeEnum} 为 LOGOUT 时
 * <p>
 * <p>
 * 接收参数
 * 默认参数:{@link AbstractLoginServiceImpl}
 * <p>
 * 去除所有登录信息
 *
 * @author 石超
 * @version v1.0.0
 */
@Service
public class LogoutLoginServiceImpl extends AbstractLoginServiceImpl<Object> implements LogoutService {
    @Resource
    private LoginLogService loginLogService;

    @Resource
    private LoginProperties loginProperties;

    @Resource
    private SessionRepository<Session> sessionRepository;

    @Override
    public List<Integer> hasParseLoginType() {
        return Collections.singletonList(LoginTypeEnum.LOGOUT.getType());
    }

    @Override
    protected Object validateLoginContext() {
        return null;
    }

    @Override
    protected boolean hasUsername() {
        return false;
    }

    @Override
    public void finishLogin() {
        LoginData loginData = getContext().getLoginData();
        loginData.setLoginType(getLoginData().getLoginType());
        loginData.setLoginFromEnum(getLoginData().getLoginFromEnum());
        loginData.setJson(getLoginData().getJson());
        loginData.setLoginIp(getLoginData().getLoginIp());

        logout(getContext().getLoginData());

        SpringContextUtil.publishEvent(new LogoutEvent(loginData));
    }

    @Override
    public void logout(Long userId, Integer loginFrom, Integer source) {
        if (userId != null && sessionRepository != null) {
            if (sessionRepository instanceof FindByIndexNameSessionRepository) {
                Map<String, Session> map = ((FindByIndexNameSessionRepository<Session>) sessionRepository).findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, userId.toString());

                for (String key : map.keySet()) {
                    Session session = map.get(key);

                    LoginData loginData = session.getAttribute(Constants.SESSION_LOGIN_DATE);

                    if (loginData != null) {
                        boolean isClient = loginData.getLoginFromEnum().isType(loginFrom);
                        boolean isSelf = Objects.equals(getContext().getSession().getId(), key);
                        boolean isSource = Objects.equals(loginData.getSource(), source);
                        boolean isMobileLogout = !isClient && (loginData.getLoginFromEnum() == LoginFromEnum.LOGIN_FROM_CLIENT_IOS || loginData.getLoginFromEnum() == LoginFromEnum.LOGIN_FROM_CLIENT_ANDROID);
                        boolean isClientLogout = isClient && loginData.getLoginFromEnum() == LoginFromEnum.LOGIN_FROM_CLIENT;

                        //不是自己, 并且是同一来源
                        if (!isSelf && isSource && (loginData.isOnlyUser() || isClientLogout || isMobileLogout)) {
                            session.setAttribute(Constants.SESSION_LOGOUT_TYPE, LogoutTypeEnum.NORMAL);
                            sessionRepository.deleteById(key);

                            log.info("清理session成功, sessionId={}, userId={}, userName={}, maxInactiveIntervalInSeconds={},", session.getId(), loginData.getUserId(), loginData.getUserName(), session.getMaxInactiveInterval().getSeconds());
                        }
                    }
                }
            }
        }
    }

    @Override
    public <T> void logout(LoginData<T> loginData) {
        log.debug("用户已经登出 username={}", loginData.getUserName());

        loginLogService.updateLogoutEndDate(loginData.getLoginLogId());
        loginData.setRedirectUrl(loginProperties.redirectLogin(getLoginData().getRedirectUrl(), getLoginData().getStyle()));

        loginData.setUser(null);
        loginData.setUserId(null);
        loginData.setUserName(null);
    }
}
